import {replaceByObj,getFisrtMatchOfMultiples} from "./xUtil.js";
import {getInitMatrix,fillMatrixByItem} from "./treeUtil.js";

const tmplBase = `<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" 
 xmlns:o="urn:schemas-microsoft-com:office:office" 
 xmlns:x="urn:schemas-microsoft-com:office:excel" 
 xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" 
 xmlns:html="http://www.w3.org/TR/REC-html40">
 <Styles>
   {{Styles}}
 </Styles>
 <Worksheet ss:Name="Sheet1">
  <Table >   
     {{Cols}}
     {{Rows}}
  </Table>
 </Worksheet>
</Workbook>
`;
const tmplStyle ='<Style ss:ID="{{StyleId}}"> {{Align}} {{Border}} {{Font}} {{Interior}}</Style>'

const tmplAlign = {center:'<Alignment ss:Horizontal="Center" ss:Vertical="Center"/>',
                      right:'<Alignment ss:Horizontal="Right" ss:Vertical="Center"/>',
                      left: '<Alignment ss:Vertical="Center"/>'};
const tmplBorder = `<Borders>
    <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1" ss:Color="{{color}}"/>
    <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"  ss:Color="{{color}}"/>
    <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"  ss:Color="{{color}}"/>
    <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"  ss:Color="{{color}}"/>
   </Borders>`;

const tmplFont = `<Font ss:FontName="Microsoft YaHei"  x:CharSet="134"  x:Family="Swiss"  ss:Size="12" ss:Color="{{color}}"/>`;
const tmplInterior = '<Interior ss:Color="{{color}}" ss:Pattern="Solid"/>';
const tmplCell = `<Cell {{ssIndex}} ss:MergeAcross="{{colspan}}" ss:MergeDown="{{rowspan}}" ss:StyleID="{{styleId}}">
                      <Data ss:Type="String">{{data}}</Data>
                   </Cell>`;
const tmplRow = '<Row> {{cells}} </Row>';
const tmplssIndex ='ss:Index="{{index}}"';
const tmplCol = '<Column ss:Width="{{width}}"/>';

const classLevels = 'level-1,level-2,level-3,level-4,level-5,level-6,level-7,level-8,level-9';

export  default class xmlTable{
    $table;
    vStyleMap = new Map();
    vBorderColor;
    vBgColor='#FFFFFF';
    vStyleID = 100;
    vYlength;
    vXlength;
    vColWidth =[];
    /*
    * 算法：
    *   1：首先要根据 table 来 构建一个 二维的Matrix
    * 
    *   合并单元格 处理 。 
    *   填充 matrix
    *   1：如果是合并单元格，就把需要的格子占据。
    *   2：table 循环时 ，每一行td 的数据 只能从不为空的格子 分发。 
    *    
    *   从matrix 渲染
    *    渲染时 ，只能渲染 未渲染的。渲染后 打标记，防止 二次渲染。
    *    xml table 只需要考虑 rowspan 的情况。
    *    每一行如果上一个 ssIndex 和 当前 的 ssIndex 不连续 则需要 设置 ssIndex 
    *    每一行 ssIndex 初始 为 0 ;
    * 
    * */
    vMatrix = [];

    init($table){
        this.$table=$table;
        this.vXlength = this.getXlength();
        this.vYlength = this.getYlength();
        this.vMatrix = getInitMatrix(this.vYlength,this.vXlength);
        this.fillMatrix();
    }


    fillMatrix(){
        let $trs = this.$table.find('tr');
        let rows ='';
        $trs.each((i,tr)=>{
            // tr 是 dom 对象
            let cells = '';
            let j =0;
            for(let td of tr.childNodes){
                let cellSpan = this.getCellSpanByDom(td);
                let cellWidth = this.getTdWidth(td);
                let cellAttr;
                if(td.childNodes[0])
                    cellAttr= this.getCellAttrByDom(td.childNodes[0]);
                let styleKey = cellAttr.align+','+cellAttr.fontColor+','+cellAttr.bgColor;
                let styleInfo = this.vStyleMap.get(styleKey);
                if(!styleInfo){
                    styleInfo={...cellAttr,styleId:this.getStyleId()};
                    this.vStyleMap.set(styleKey,styleInfo);
                }
                let item = {...cellSpan,...cellAttr,cellWidth,styleId:styleInfo.styleId};

                /*
                获取j  判读i行 matrix [i][j]点 是 是否为空，不为空则是 被占据了， 一直取到取下一个不为空的点位置
                * */
                while(true){
                    if(this.vMatrix[i][j])
                        j=j+1;
                    else
                        break;
                }
                if(item.colspan>1 || item.rowspan>1)
                    fillMatrixByItem(this.vMatrix,[i,i+item.rowspan-1],[j,j+item.colspan-1],item);
                else
                    this.vMatrix[i][j] = item;
                j +=item.colspan;
            }

        });
        return rows;
    }

    getXlength(){
        let i =0 ;
        let tds = this.$table.find('tr')[0].childNodes;
        for(let td of tds){
            i +=this.getCellSpanByDom(td).colspan;
        }
        return i;
    }

    getYlength(){
        return this.$table.find('tr').length;
    }
    getStyleId(){
        this.vStyleID += 1;
        return 's'+this.vStyleID;
    }

    getHexColor(rgb){
        function  hex(n){
            return n < 16 ? '0' + (+n).toString(16) : (+n).toString(16);
        }
        return rgb.replace(/^rgb\((\d+)\,\s*(\d+)\,\s*(\d+)\)$/g, function(a, r, g, b){
            return '#' + hex(r) + hex(g) + hex(b);
        });
    }

    setBorderColor(){
         this.vBorderColor = this.getHexColor(this.$table.css('border-top-color'));
         this.vBorderColor =this.vBorderColor||'#000000';
    }

    setBgColor(){
        let p = this.$table[0];
        let color;
        while(true){
            let style =p.currentStyle || document.defaultView.getComputedStyle(p, null);
            color = style.backgroundColor;
            if(p.nodeName =='BODY')
                break;
            if(color=='rgba(0, 0, 0, 0)')
                p=p.parentNode;
            else
                break;
        }
        if(color!='rgba(0, 0, 0, 0)')
             this.vBgColor = this.getHexColor(color);
    }

    getCellSpanByDom(td){
        let colspan = td.getAttribute('colspan');
        let rowspan =  td.getAttribute('rowspan');
        colspan =colspan?colspan-0:1;
        rowspan = rowspan?rowspan-0:1;
        return {colspan,rowspan};
    }

    getTdWidth(td){
        return td.offsetWidth;
    }

    getCellAttrByDom(itemDom){
        let itemClass = itemDom.getAttribute("class");
        let align ='left';
        if(itemClass)
             align = itemClass.indexOf("right")>-1?'right':itemClass.indexOf("center")>-1?'center':'left';
        let data='';
        // 导入到xls 中  缩进 用空格来代替
        let spaceBeforeData='';
        let fontColor ='#000000';
        let DataDom;
        let bgColor  =this.vBgColor||'#FFFFFF';
        for(let sDom of itemDom.childNodes){
            switch(sDom.nodeName){
                case 'SPAN':
                    let sDomClass=sDom.getAttribute("class");
                    if(sDomClass){
                        let level = getFisrtMatchOfMultiples(sDomClass.split(' '),classLevels);
                        if(level){
                            spaceBeforeData = new Array(level.replace('level-','')-0).join('      ');
                            align = 'left';
                        }

                    }
                    data += sDom.innerHTML?(sDom.innerHTML+' '):'';
                    DataDom = sDom;
                    break;
                case 'TEXTAREA':
                    data += sDom.value;
                    DataDom = sDom;
                default:
                    data +='';
            }
        }

        if(DataDom){
            let style =DataDom.currentStyle || document.defaultView.getComputedStyle(DataDom, null);
            fontColor = this.getHexColor(style.color);
            let p = DataDom;
            let color;
            while(true){
                if(p.nodeName =='TABLE')
                    break;
                let style =p.currentStyle || document.defaultView.getComputedStyle(p, null);
                color = style.backgroundColor;
                if(color=='rgba(0, 0, 0, 0)')
                    p=p.parentNode;
                else
                    break;
            }
            if(color!='rgba(0, 0, 0, 0)')
                bgColor = this.getHexColor(color)||'#FFFFFF';
        }


        if(spaceBeforeData)
            data = spaceBeforeData+data;
        return {data,fontColor,bgColor,align}
    }

    renderItem(item,i,j){
        let ssIndex =tmplssIndex.replace('{{index}}',j+1);
        return replaceByObj(tmplCell,{rowspan:item.rowspan-1,colspan:item.colspan-1,
                                       data:item.data,styleId:item.styleId,ssIndex});
    }

    renderTable(){
        let rows ='';
        for(let i=0;i<this.vYlength;i++){
            let cells ='';
            for(let j=0;j<this.vXlength;j++){
                if(this.vMatrix[i][j] && (!this.vMatrix[i][j]._in)){
                    let item = this.vMatrix[i][j];
                    cells += this.renderItem(item,i,j);
                    item._in= true;
                }

                //获取col 宽度 ，最小替代法
                if( !this.vColWidth[j] || this.vColWidth[j]>this.vMatrix[i][j].cellWidth)
                    this.vColWidth[j] = this.vMatrix[i][j].cellWidth;
            }
            rows += replaceByObj(tmplRow,{cells});
        }
        return rows;
    }

    renderCols(){
        let cols='';
        for(let width of this.vColWidth){
            cols += replaceByObj(tmplCol,{width});
        }
        return cols;
    }

    //const tmplStyle ='<Style ss:ID="{{StyleId}}"> {{Align}} {{Border}} {{Font}} {{Interior}}</Style>'
    renderStyle(){
        let styles ='';
        this.vStyleMap.forEach((styleInfo,styleKey) =>{
            let Border = replaceByObj(tmplBorder,{color:this.vBorderColor});
            let Align = tmplAlign[styleInfo.align];
            let StyleId = styleInfo.styleId;
            let Font = replaceByObj(tmplFont,{color:styleInfo.fontColor});
            let Interior = replaceByObj(tmplInterior,{color:styleInfo.bgColor});
            styles += replaceByObj(tmplStyle,{Border,Align,StyleId,Font,Interior});
        })
        return styles;
    }

    toXmlTable($table){
        this.init($table);
        this.setBorderColor();
        this.setBgColor();
        let Rows = this.renderTable();
        let Styles =  this.renderStyle();
        return replaceByObj(tmplBase,{Rows,Styles,Cols: this.renderCols()});
    }
}
